/*
 * @(#)BinaryCollectionUtils.java   1.0   May 18, 2007
 *
 * Copyright 2000-2007 ETH Zurich. All Rights Reserved.
 *
 * This software is the proprietary information of ETH Zurich.
 * Use is subject to license terms.
 *
 * @(#) $Id: BinaryCollectionUtils.java 435 2007-05-23 12:54:49Z D\aldespin $
 */
package ch.ethz.globis.javaom.utils;

import java.util.ArrayList;
import java.util.Collection;

import ch.ethz.globis.javaom.core.AttributeValue;
import ch.ethz.globis.javaom.core.Bulk;
import ch.ethz.globis.javaom.core.Object;
import ch.ethz.globis.javaom.core.Tuple;
import ch.ethz.globis.javaom.system.Metadata;

/**
 * @author aldespin
 * @version 1.0
 */
public class BinaryCollectionUtils {

   public static boolean add(final Object binaryCollection, final Object domain, final Object range) {
      return BinaryCollectionUtils.getExtent(binaryCollection).add(new Tuple(domain, range));
   }

   public static boolean remove(final Object binaryCollection, final Object domain,
         final Object range) {
      final Collection<Tuple> toRemove = new ArrayList<Tuple>();
      Tuple tuple;
      for (Object current : BinaryCollectionUtils.getExtent(binaryCollection)) {
         tuple = (Tuple) current;
         if (tuple.domain().equals(domain) && tuple.range().equals(range)) {
            toRemove.add(tuple);
         }
      }
      return BinaryCollectionUtils.getExtent(binaryCollection).removeAll(toRemove);
   }

   @SuppressWarnings("unchecked")
   public static AttributeValue<String> getName(final Object binaryCollection) {

      final Bulk argumentHasAttributeValues = ObjectUtils.getAttributeValues(binaryCollection);

      // get name attribute object
      final Object nameAttribute =
            Metadata.METADATA.getAttributeObject(Metadata.COLLECTION, Metadata.NAME);

      // get links of name attribute
      final Bulk nameValues =
            AttributeUtils.getAttributeValues(argumentHasAttributeValues, nameAttribute);

      // test if not empty
      if (nameValues.isEmpty()) {
         throw new RuntimeException("Binary Collection '" + binaryCollection.toString()
               + "' given has no name attribute value");
      }
      // test if no more than one value
      if (nameValues.size() > 1) {
         throw new RuntimeException("Binary Collection '" + binaryCollection.toString()
               + "' given has more than one name attribute value");
      }

      // return result
      try {
         return (AttributeValue<String>) nameValues.iterator().next();
      } catch (final ClassCastException e) {
         throw new RuntimeException("Binary Collection '" + binaryCollection.toString()
               + "' given has a name attribute value of type '"
               + nameValues.iterator().next().getClass().getSimpleName()
               + "' instead of AttributeValue<String>");
      }
   }

   public static Object getDomainMemberType(final Object binaryCollection) {

      final Bulk argumentHasAttributeValues = ObjectUtils.getAttributeValues(binaryCollection);

      // get domain membertype attribute object
      final Object domainMemberTypeAttribute =
            Metadata.METADATA.getAttributeObject(Metadata.BINARYCOLLECTION,
                  Metadata.DOMAIN_MEMBERTYPE);

      // get links of domain membertype attribute
      final Bulk domainMembertypeValues =
            AttributeUtils.getAttributeValues(argumentHasAttributeValues,
                  domainMemberTypeAttribute);

      // test if not empty
      if (domainMembertypeValues.isEmpty()) {
         throw new RuntimeException("Binary Collection '" + binaryCollection.toString()
               + "' given has no domain membertype attribute value");
      }
      // test if no more than one value
      if (domainMembertypeValues.size() > 1) {
         throw new RuntimeException("Binary Collection '" + binaryCollection.toString()
               + "' given has more than one domain membertype attribute value");
      }

      // return result
      return domainMembertypeValues.iterator().next();
   }

   public static Object getRangeMemberType(final Object binaryCollection) {

      final Bulk argumentHasAttributeValues = ObjectUtils.getAttributeValues(binaryCollection);

      // get range membertype attribute object
      final Object rangeMemberTypeAttribute =
            Metadata.METADATA.getAttributeObject(Metadata.BINARYCOLLECTION,
                  Metadata.RANGE_MEMBERTYPE);

      // get links of range membertype attribute
      final Bulk rangeMembertypeValues =
            AttributeUtils
                  .getAttributeValues(argumentHasAttributeValues, rangeMemberTypeAttribute);

      // test if not empty
      if (rangeMembertypeValues.isEmpty()) {
         throw new RuntimeException("Binary Collection '" + binaryCollection.toString()
               + "' given has no range membertype attribute value");
      }
      // test if no more than one value
      if (rangeMembertypeValues.size() > 1) {
         throw new RuntimeException("Binary Collection '" + binaryCollection.toString()
               + "' given has more than one range membertype attribute value");
      }

      // return result
      return rangeMembertypeValues.iterator().next();
   }

   public static Bulk getExtent(final Object binaryCollection) {

      final Bulk argumentHasAttributeValues = ObjectUtils.getAttributeValues(binaryCollection);

      // get extent attribute object
      final Object extentAttribute =
            Metadata.METADATA.getAttributeObject(Metadata.COLLECTION, Metadata.EXTENT);

      // get links of extent attribute
      final Bulk extentValues =
            AttributeUtils.getAttributeValues(argumentHasAttributeValues, extentAttribute);

      // test if not empty
      if (extentValues.isEmpty()) {
         throw new RuntimeException("Binary Collection '" + binaryCollection.toString()
               + "' given has no extent attribute value");
      }
      // test if no more than one value
      if (extentValues.size() > 1) {
         throw new RuntimeException("Binary Collection '" + binaryCollection.toString()
               + "' given has more than one extent attribute value");
      }

      // return result
      try {
         return (Bulk) extentValues.iterator().next();
      } catch (final ClassCastException e) {
         throw new RuntimeException("Binary Collection '" + binaryCollection.toString()
               + "' given has an extent attribute value of type '"
               + extentValues.iterator().next().getClass().getSimpleName() + "' instead of Bulk");
      }
   }

   public static Bulk domain(final Object binaryCollection) {
      // TODO maybe return a collection object?
      return BinaryCollectionUtils.domain(BinaryCollectionUtils.getExtent(binaryCollection));
   }

   public static Bulk range(final Object binaryCollection) {
      // TODO maybe return a collection object?
      return BinaryCollectionUtils.range(BinaryCollectionUtils.getExtent(binaryCollection));
   }

   /**
    * Returns all tuples that contain domain as domain value
    * 
    * @param binaryCollection
    * @param domain
    * @return
    */
   public static Bulk domainSingleRestriction(final Object binaryCollection, final Object domain) {
      // TODO maybe return a collection object?
      return BinaryCollectionUtils.domainRestriction(BinaryCollectionUtils
            .getExtent(binaryCollection), domain);
   }

   /**
    * Returns all tuples that contain range as range value
    * 
    * @param binaryCollection
    * @param range
    * @return
    */
   public static Bulk rangeSingleRestriction(final Object binaryCollection, final Object range) {
      // TODO maybe return a collection object?
      return BinaryCollectionUtils.rangeRestriction(BinaryCollectionUtils
            .getExtent(binaryCollection), range);
   }

   /**
    * Returns all tuples that contain any member of domainCollection as domain value
    * 
    * @param binaryCollection
    * @param domain
    * @return
    */
   public static Bulk domainRestriction(final Object binaryCollection,
         final Object domainCollection) {
      // TODO maybe return a collection object?
      return BinaryCollectionUtils.domainRestriction(BinaryCollectionUtils
            .getExtent(binaryCollection), BinaryCollectionUtils.getExtent(domainCollection));
   }

   /**
    * Returns all tuples that contain any member of rangeCollection as range value
    * 
    * @param binaryCollection
    * @param range
    * @return
    */
   public static Bulk rangeRestriction(final Object binaryCollection, final Object rangeCollection) {
      // TODO maybe return a collection object?
      return BinaryCollectionUtils.rangeRestriction(BinaryCollectionUtils
            .getExtent(binaryCollection), BinaryCollectionUtils.getExtent(rangeCollection));
   }

   private static Bulk domain(final Bulk extent) {
      final Bulk result = new Bulk();
      for (Object current : extent) {
         result.add(((Tuple) current).domain());
      }
      return result;
   }

   public static Bulk range(final Bulk extent) {
      final Bulk result = new Bulk();
      for (Object current : extent) {
         result.add(((Tuple) current).range());
      }
      return result;
   }

   public static Bulk domainRestriction(final Bulk extent, final Object domain) {
      final Bulk result = new Bulk();
      Tuple tuple;
      for (Object current : extent) {
         tuple = (Tuple) current;
         if (tuple.domain().equals(domain)) {
            result.add(tuple);
         }
      }
      return result;
   }

   private static Bulk rangeRestriction(final Bulk extent, final Object range) {
      final Bulk result = new Bulk();
      Tuple tuple;
      for (Object current : extent) {
         tuple = (Tuple) current;
         if (tuple.range().equals(range)) {
            result.add(tuple);
         }
      }
      return result;
   }

   private static Bulk domainRestriction(final Bulk extent, final Bulk domain) {
      final Bulk result = new Bulk();
      Tuple tuple;
      for (Object current : extent) {
         tuple = (Tuple) current;
         if (domain.contains(tuple.domain())) {
            result.add(tuple);
         }
      }
      return result;
   }

   private static Bulk rangeRestriction(final Bulk extent, final Bulk range) {
      final Bulk result = new Bulk();
      Tuple tuple;
      for (Object current : extent) {
         tuple = (Tuple) current;
         if (range.contains(tuple.range())) {
            result.add(tuple);
         }
      }
      return result;
   }
}
